
/*
 * @项目名称: 模板开发
 * @FilePath: /meek/src/components/modify.js
 * @Author: 1106408264@qq.com
 * @Date: 2017-05-25T14:56:27+08:00
 * @LastEditors: yangxianghong@datatang.com
 * @LastEditTime: 2022-12-26 10:37:52
 * Copyright (c) 2018-2022 
 */

/**
 * Created by zhangyong on 2017/5/25.
 */

import Component from './component'

import BrowserEvent from '../meek/browserevent'
import Geometry from '../geometry/geometry'
import Point from '../geometry/point'
import Parallelogram from '../geometry/parallelogram'
import TiltRectangle from '../geometry/tiltrectangle'
import Trapezium from '../geometry/trapezium'
import SquareX from '../geometry/squarex'
import Cube from '../geometry/cube'

import { listen, unlistenByKey } from '../core/eventmanager'
import { EventType } from '../meek/eventtype'

import ModifyEvent from './modifyevent'

import Feature from '../meek/feature'
import FeatureLayer from '../lyr/featurelayer'
import { Style } from '../style/style'
import { ExtentUtil } from '../geometry/support/extentutil'

import {
  closestOnSegment, squaredDistanceToSegment,
  squaredDistance, distance
} from '../geometry/support/geometryutil'

import { noModifierKeys, singleClick } from '../utils/mousekey'
import { functions } from '../utils/functions'
import Circle from './../geometry/circle';

import cloneDeep from 'lodash/cloneDeep'

import PointStyle from './../style/pointstyle';
import FillStyle from './../style/fillstyle';
import LineStyle from './../style/linestyle';

/**
 * 编辑模式，启动后可以编辑图形
 *
 * @class Modify
 * @extends Component
 * @module component
 * @constructor
 * @example
 *
 *      // 实例化编辑工具
 *      var modifyTool = new Datatang.Modify({
 *        features: selectedFeaturs
 *      })
 */
export default class Modify extends Component {

  /**
   * 构造函数
   *
   * @constructor constructor
   * @param options
   */
  constructor(options = {}) {
    super(options)

    this.applyHandleEventOption({
      handleDownEvent: this._handleDownEvent,
      handleDragEvent: this._handleDragEvent,
      handleUpEvent: this._handleUpEvent
    })

    /**
     * 可以编辑的feature集合
     *
     * @property features
     * @type {Array}
     * @private
     */
    this._features = []

    /**
     * 当前鼠标点与图形之间的公差距离，小于该值后立即捕捉标注数据
     *
     * @property pixelTolerance
     * @type {number}
     * @private
     */
    this._pixelTolerance = options.pixelTolerance ?
      options.pixelTolerance : 10

    /**
     * 可以整体移动图形，矩形默认可以整体移动
     *
     * @property movableGeometrys
     * @type {[*]}
     * @private
     */
    this._movableGeometrys = options.movableGeometrys ?
      options.movableGeometrys : ['extent']

    this._retotableGeometrys = options.retotableGeometrys ?
      options.retotableGeometrys : ['TiltRectangle', 'Ellipse', 'parallelogram']

    /**
     *
     * hasMoveableGeometrys
     * @type {boolean}
     * @private
     */
    this._hasMoveableGeometrys = true

    const movableGeometrys = this._movableGeometrys
    if (movableGeometrys === null || movableGeometrys === undefined) {
      this._hasMoveableGeometrys = false
    }

    if (Array.isArray(movableGeometrys)) {
      if (movableGeometrys.length === 0) {
        this._hasMoveableGeometrys = false
      }
    }
    /**
     *
     * @type {Set}
     * @private
     */
    this.movableGeometrySet

    if (this._hasMoveableGeometrys) {
      this.movableGeometrySet = new Set(this._movableGeometrys)
    }

    /**
     * @private
     */
    this._deleteCondition = options.deleteCondition ?
      options.deleteCondition : this._defaultDeleteCondition

    /**
     * 允许顶点编辑的条件方法，默认可编辑
     * @private
     */
    this._vertexEditableCondition = options.vertexEditableCondition ?
      options.vertexEditableCondition : functions.TURE

    /**
     * 允许边编辑的条件方法，默认可编辑
     * @private
     */
    this._edgeEditableCondition = options.edgeEditableCondition ?
      options.edgeEditableCondition : functions.TURE

    /**
     * Determine if should snap to vertexs of gemetry
     *
     * snappedToVertex
     * @type {boolean}
     * @private
     */
    this._snappedToVertex = false

    /**
     * @snapSegments
     * @type {null}
     * @private
     */
    this._snapSegments = null

    /**
     * @vertexFeature
     * @type {null}
     * @private
     */
    this._vertexFeature = null

    /**
     * The editable features
     *
     * 设置需要编辑图形的集合，
     * 如果图形不允许编辑，请从该集合移除
     *
     * @property features
     * @type {[]}
     */
    this.features = options.features || []

    /**
     *
     * @type {[*]}
     * @private
     */
    this._lastPixel = [0, 0]


    /**
     * 初始化草稿图层，用于临时高亮显示编辑的图形
     *
     * @property overLayer
     * @type {FeatureLayer}
     * @private
     */
    this._overLayer = new FeatureLayer({
      style: options.style ? options.style : this.getDefaultStyleFunction(),
      zIndex: 1000
    })

    this.active = true


    this._dragSegments = []

    /**
     * 图形是否修改过，默认为false
     *
     * modified
     * @type {boolean}
     * @private
     */
    this._modified = false

    /**
     * changingFeature
     * @type {boolean}
     * @private
     */
    this._changingFeature = true

    /**
     * Tracks if the next `singleclick` event should be ignored to prevent
     * accidental deletion right after vertex creation.
     *
     * ignoreNextSingleClick
     * @type {boolean}
     * @private
     */
    this._ignoreNextSingleClick = false

    /**
     * shouldAddToVertexs
     * @type {boolean}
     * @private
     */
    this._shouldAddToVertexs = false

    /**
     * insertVertices
     * @type {Array}
     * @private
     */
    this._insertVertices = []

    /**
     * Keep the mouse-down point
     *
     * downPoint
     * @type {null}
     * @private
     */
    this._downPoint = null

    /**
     * Keep the current moved geometry
     *
     * currentMovedGeometry
     * @type {null}
     * @private
     */
    this._currentMovedGeometry = null

    /**
     * 记录上一次被编辑的点
     * 一般指的是在边上的捕捉点或者顶点本身
     * 只是用于平行四边形的编辑使用
     * @type {null}
     * @private
     */
    this._oldEditedVertex = null


    this._isEditCubic = false;

    this.bezierCondition = options.bezierCondition ? options.bezierCondition : functions.TURE;

    this.copyCondition = options.copyCondition ? options.copyCondition : functions.TURE;

    listen(document, 'keyup', (e) => {
      this.isEditCubic = false;
    }, this);
    listen(document, 'keydown', (e) => {
      if (e.code === 'KeyC') {
        this.isEditCubic = true;
      }
    }, this);

    this._displayExtentLwText = options.displayExtentLwText !== undefined ? options.displayExtentLwText : true
    this._extentLwTextStyle = new PointStyle(
      8,
      [0, 0, 0],
      0,
      new FillStyle(
        [0, 0, 0],
        new LineStyle(
          [0, 0, 0],
          1,
          1.25
        ),
        0
      )
    )

  }


  get isEditCubic() {
    return this._isEditCubic;
  }

  set isEditCubic(val) {
    this._isEditCubic = val;
  }


  /**
   * feature的读写器，读取设置当前的feature
   *
   * @property features
   * @returns {Object} feature
   */
  get features() { return this._features }
  set features(value = []) {
    this._features = value
    this._snapSegments = null

    if (this._vertexFeature) {
      this._overLayer.removeFeature(this._vertexFeature)
      this._vertexFeature = null
    }

    this._hasMoveableGeometrys =
      value.length === 0 ? false : true

    this._dragSegments = []
    this._snappedToVertex = false
  }

  /**
   * Handle the mouse events emitted from map.
   *
   * 处理鼠标事件
   *
   * handleMouseEvent
   * @param browserEvent
   * @returns {boolean}
   */
  handleMouseEvent(browserEvent) {
    if (!(browserEvent instanceof BrowserEvent)) {
      return true
    }

    browserEvent.coordinate = this.autoAdsorbent(this.coordinateBeyond(browserEvent))

    browserEvent.originalCoordinate = this.map.getCoordinateFromPixel(browserEvent.pixel);

    this.lastPointerEvent_ = browserEvent

    let handled
    if (browserEvent.type == BrowserEvent.MOUSE_MOVE &&
      !this.handlingDownUpSequence) {
      this._handlePointerMove(browserEvent)
    }
    if (this._vertexFeature && this._deleteCondition(browserEvent)) {
      if (browserEvent.type !== BrowserEvent.SINGLE_CLICK ||
        !this._ignoreNextSingleClick) {
        handled = this.removePoint()
      } else {
        handled = true
      }
    }

    if (browserEvent.type == BrowserEvent.SINGLE_CLICK) {
      this._ignoreNextSingleClick = false
    }

    return super.handleMouseEvent(browserEvent) && !handled
  }

  /**
   * Removes the vertex currently being pointed.
   *
   *
   * removePoint
   * @return {boolean} True when a vertex was removed.
   * @api
   */
  removePoint() {
    if (this._lastPointerEvent && this._lastPointerEvent.type != BrowserEvent.MOUSE_DRAG) {
      let evt = this.lastPointerEvent_
      this.willModifyFeatures_(evt)
      this._removeVertex()

      this.dispatchEvent(new ModifyEvent(
        ModifyEvent.EventType.MODIFY_END, this.features, evt))
      this._modified = false
      return true
    }

    return false
  }

  /**
   * compareIndexes
   * @param a
   * @param b
   * @returns {number}
   * @private
   */
  _compareIndexes(a, b) {
    return a.index - b.index
  }

  /**
   * willModifyFeatures
   * @param event
   * @private
   */
  _willModifyFeatures(event) {
    if (!this._modified) {
      this._modified = true
      this.dispatchEvent(new ModifyEvent(ModifyEvent.EventType.MODIFY_START,
        this.features, event))
    }
  }

  _handleCubicEvent(evt) {
    this._downPoint = evt.coordinate
    this._dragSegments.length = 0
    this._modified = false
    const vertex = evt.coordinate
    const vertexFeature = this._vertexFeature
    if (vertexFeature) {
      //const insertVertices = [this._snapSegments]
      const insertVertices = this._snapSegments.filter(item => {
        return [Geometry.POLYGON, Geometry.LINE].includes(item.geometry.geometryType);
      })
      if (insertVertices.length) {
        this._modified = true
        this.dispatchEvent(
          new ModifyEvent(ModifyEvent.EventType.MODIFY_START,
          insertVertices.map(item => item.feature).reverse(),
          evt
        ))
      }

      if (insertVertices.length) {
        for (let j = insertVertices.length - 1; j >= 0; --j) {
          const obj = insertVertices[j];
          if (
            [Geometry.POLYGON, Geometry.LINE].includes(obj.geometry.geometryType)
          ) {
            const geometryType = obj.geometry.geometryType;
            const lineMode = obj.geometry.lineMode
            let coords = this._getControlPoint(obj.segment[0], obj.segment[1])
            let hasModify = false;

            if (geometryType === Geometry.LINE) {
              // 直线禁止添加贝塞尔曲线
              if(lineMode === 2 || lineMode === 3) return
              if (obj.geometry.hasBezier(obj.index)) {
                obj.geometry.removeBezier({
                  start: obj.index
                })
              } else {
                obj.geometry.addBezier({
                  start: obj.index,
                  points: coords
                })
              }
              hasModify = true;
            }
            if (geometryType === Geometry.POLYGON) {
              if (obj.geometry.hasBezier(obj.ringIndex, obj.index)) {
                obj.geometry.removeBezier({
                  start: obj.index,
                  points: coords,
                  ringIndex: obj.ringIndex
                })
              } else {
                obj.geometry.addBezier({
                  start: obj.index,
                  points: coords,
                  ringIndex: obj.ringIndex
                })
              }
              hasModify = true;
            }
            if (hasModify) {
              this.dispatchEvent(
                new ModifyEvent(
                  ModifyEvent.EventType.MODIFY_END,
                  [obj.feature],
                  evt
                )
              )
            }

          }
        }
      }
    }
  }


  _getControlPoint(coord1, coord2) {
    return [
      [
        coord1[0] + 1 / 3 * (coord2[0] - coord1[0]),
        coord1[1] + 1 / 3 * (coord2[1] - coord1[1])
      ],
      [
        coord1[0] + 2 / 3 * (coord2[0] - coord1[0]),
        coord1[1] + 2 / 3 * (coord2[1] - coord1[1])
      ]
    ]
  }



  /**
   * Handle poiner mouse down event
   *
   * handleDownEvent
   * @param evt {Event}
   * @private
   */
  _handleDownEvent(evt) {
    if (this.bezierCondition(evt) && this.isEditCubic) {
      return this._handleCubicEvent(evt);
    }

    this._downPoint = evt.coordinate
    this._dragSegments.length = 0
    this._modified = false
    const coordinate = evt.coordinate
    const vertexFeature = this._vertexFeature

    if (evt.originalEvent.shiftKey) {
      // 判断当前是否选中线
      const filters = this.features.filter(feature => {
        const geometry = feature.geometry
        return this.movableGeometrySet.has(geometry.geometryType) &&
          geometry.containsXY(
            coordinate[0],
            coordinate[1],
            {
              tolerance: this._pixelTolerance
            }
          )
      });
      const f = filters[0];
      if (f && f.geometry.geometryType === Geometry.LINE) {
        this._currentMovedGeometry = filters[0]
        this.dispatchEvent(new ModifyEvent(ModifyEvent.EventType.MODIFY_START,
          [this._currentMovedGeometry], event))
          if (vertexFeature) {
            this._overLayer.removeFeature(this._vertexFeature)
          }
         return true
      }
    }
    if (vertexFeature) {
      //const insertVertices = [this._snapSegments]
      const insertVertices = this._snapSegments
      if (insertVertices.length) {
        this._willModifyFeatures(evt)
      }

      this._shouldAddToVertexs = true
      this._insertVertices = this._snapSegments

      return !!this._vertexFeature
    }
    // Move the selected geometry
    else {
      if (!this._hasMoveableGeometrys || evt.originalEvent.button === 2) {
        return
      }

      const filters = this.features.filter(feature => {
        const geometry = feature.geometry
        return this.movableGeometrySet.has(geometry.geometryType) &&
          geometry.containsXY(coordinate[0], coordinate[1])
      })

      // We get the first filter geometry to move
      // Otherwise, you should reselect
      if (filters.length > 0) {
        this._currentMovedGeometry = filters[0]

        this.dispatchEvent(new ModifyEvent(ModifyEvent.EventType.MODIFY_START,
         [this._currentMovedGeometry], event))
        return true
      }

      return false
    }
  }

  /**
   * Handle pointer move event
   *
   * 处理点移动事件
   *
   * handlePointerMove
   * @param evt {Event}
   * @private
   */
  _handlePointerMove(evt) {
    this._lastPixel = evt.pixel
    this._handlePointerAtPixel(evt.pixel, evt.map)
  }

  /**
   * @param pixel {Array}
   * @param map {map}
   * @private
   */
  _handlePointerAtPixel(pixel, map) {
    const pixelCoordinate = map.getCoordinateFromPixel(pixel)

    const sortByDistance = (s1, s2) => {
      const d1 = this._pointDistanceToSegment(pixelCoordinate, s1)
      const d2 = this._pointDistanceToSegment(pixelCoordinate, s2)
      return d1 - d2
    }

    //this._snapSegments = null
    this._snapSegments = []

    if (this.features.length === 0) {
      return
    }
    // 与当前屏幕内图形做碰撞检测
    const nodes = this._getInExtent(this.features, pixelCoordinate, this._pixelTolerance)

    const bezierNodes = nodes.filter(node => node.bezier);

    if (nodes.length > 0) {
      if (bezierNodes.length) {
        bezierNodes.forEach(node => {
          if (this._vertexEditableCondition(node)) {
            this._snappedToVertex = true;
            this._snapSegments.push(node);
            this._createOrUpdateVertexFeature(node.bezierVertex.vertex)
          }
        })
        return ;
      }
      nodes.sort(sortByDistance)
      nodes.forEach((node, index) => {
        //const node = nodes[index]
        const geometry = node.geometry
        const geometryType = geometry.geometryType
        const closestSegment = node.segment

        let vertex = this._closestOnSegment(pixelCoordinate, node)// 计算鼠标点在线段上的投影点
        const vertexPixel = map.getPixelFromCoordinate(vertex)
        // 判断当前是否选中了边
        let dist = distance(pixel, vertexPixel)

        if (geometryType === Geometry.CIRCLE) {
          if (this._edgeEditableCondition(node)) {
            let vertex = geometry.getVertex(pixelCoordinate, this._pixelTolerance)
            if (vertex) {
              node.index = 1;
              this._snapSegments.push(node);
              this._createOrUpdateVertexFeature(vertex)
            }
          }
          return ;
        }
        if (geometryType === Geometry.ELLIPSE) {
          let {coords, index} = geometry.getVertex(pixelCoordinate, this._pixelTolerance);
          if (index !== -1) {
            node.index = index;
            this._snapSegments.push(node);
            this._createOrUpdateVertexFeature(coords)
          }
          return ;
        }
        if (dist <= this._pixelTolerance) {
          /**
           *  已经选中了边
           */
          //this._snapSegments = node

          let vertexEditable = false

          if (geometryType === Geometry.CIRCLE) {

          } else {

            /**
             * 再计算是否选中了线段的两个顶点
             */

            // 判断是否允许顶点可编辑
            if (this._vertexEditableCondition(node)) {
              const pixel1 = map.getPixelFromCoordinate(closestSegment[0])
              const pixel2 = map.getPixelFromCoordinate(closestSegment[1])
              const squaredDist1 = squaredDistance(vertexPixel[0], vertexPixel[1], pixel1[0], pixel1[1])
              const squaredDist2 = squaredDistance(vertexPixel[0], vertexPixel[1], pixel2[0], pixel2[1])
              dist = Math.sqrt(Math.min(squaredDist1, squaredDist2))

              // 判断当前是否选中了顶点
              this._snappedToVertex = dist <= this._pixelTolerance
              if (this._snappedToVertex) {
                vertex = squaredDist1 > squaredDist2 ?
                  closestSegment[1] : closestSegment[0]

                const coordinateIndex = geometry.getCoordinateIndex(vertex)
                if (geometryType === Geometry.POLYGON) {
                  //console.log("----")
                  //this._snapSegments.index = coordinateIndex.index
                  //this._snapSegments.ringIndex = coordinateIndex.ringIndex
                  node.index = coordinateIndex.index
                  node.ringIndex = coordinateIndex.ringIndex
                } else if (geometryType === Geometry.PARALLELOGRAM) {
                  node.index = coordinateIndex
                  node.ringIndex = coordinateIndex
                } else if (geometryType === Geometry.TILTRECTANGLE || geometryType === Geometry.TRAPEZIUM || geometryType === Geometry.SQUAREX) {
                  //this._snapSegments.index = coordinateIndex
                  //this._snapSegments.ringIndex = coordinateIndex
                  //this._snapSegments.Vertex = this._snappedToVertex
                  node.index = coordinateIndex
                  node.ringIndex = coordinateIndex
                  node.Vertex = this._snappedToVertex
                } else if (geometryType === Geometry.MULTI_POLYGON || geometryType === Geometry.CUBE) {
                  //this._snapSegments.index = coordinateIndex.index
                  //this._snapSegments.ringIndex = coordinateIndex.ringIndex
                  //this._snapSegments.polygonIndex = coordinateIndex.polygonIndex
                  node.index = coordinateIndex.index
                  node.ringIndex = coordinateIndex.ringIndex
                  node.polygonIndex = coordinateIndex.polygonIndex
                } else {
                  //this._snapSegments.index = coordinateIndex
                  node.index = coordinateIndex
                }

                //this._snapSegments.isVertex = true
                node.isVertex = true
                vertexEditable = true
              }
            }
            if (geometryType === Geometry.PARALLELOGRAM) {
              this._snapSegments.index = node.index
              this._snapSegments.ringIndex = node.ringIndex
            }
            if (geometryType === Geometry.TILTRECTANGLE || geometryType === Geometry.TRAPEZIUM || geometryType === Geometry.SQUAREX) {
              this._snapSegments.index = node.index
              this._snapSegments.ringIndex = node.ringIndex
            }
          }

          // 边可编辑，或者顶点可编辑
          if (this._edgeEditableCondition(node) || vertexEditable) {
            const gType = node.geometry.geometryType;
            if (!node.isVertex && [Geometry.POLYGON, Geometry.LINE].includes(gType)) {
              let canEdit = true;
              if (gType === Geometry.POLYGON) {
                if (node.geometry.hasBezier(node.ringIndex, node.index)) {
                  canEdit = false;
                }
              }
              if (gType === Geometry.LINE) {
                if (node.geometry.hasBezier(node.index)) {
                  canEdit = false;
                }
              }
              if (canEdit) {
                this._snapSegments.push(node);
                this._createOrUpdateVertexFeature(vertex)
              }
            } else {
              this._snapSegments.push(node);
              this._createOrUpdateVertexFeature(vertex)
            }

            // draw the snapping point

          } else {
            //this._snapSegments = null
          }


          return
        }
      })
      if (!this._snapSegments.length) {
        if (this._vertexFeature) {
          this._overLayer.removeFeature(this._vertexFeature)
          this._vertexFeature = null
        }
      }
      return
    }

    if (this._vertexFeature) {
      this._overLayer.removeFeature(this._vertexFeature)
      this._vertexFeature = null
    }
  }

  /**
   * Handle mouse drag event.
   *
   * handleDragEvent
   * @param evt {Event}
   * @private
   */
  _handleDragEvent(evt) {
    if (this.copyCondition(evt)) {
      return this.dispatchEvent(new ModifyEvent(ModifyEvent.EventType.MODIFY_MOUSE_DRAG, undefined, evt))
    }
    this._ignoreNextSingleClick = false
    let vertex = evt.coordinate
    if (this._shouldAddToVertexs && this._vertexFeature) {

      const vertexFeature = this._vertexFeature
      const vertex = vertexFeature.geometry.getCoordinates()

      const insertVertices = this._insertVertices
      for (let j = insertVertices.length - 1; j >= 0; --j) {
        this._insertVertex(insertVertices[j], vertex);
        this._ignoreNextSingleClick = true;
      }

      this._shouldAddToVertexs = false
    }

    const dragSegments = this._dragSegments
    const len = dragSegments.length

    this.dispatchEvent(new ModifyEvent(ModifyEvent.EventType.MODIFYING, undefined, evt))
    // move the edge of selected geometry
    if (len !== 0) {
      this._willModifyFeatures(evt)

      for (let i = 0; i < len; ++i) {
        const dragSegment = dragSegments[i]
        const segmentData = dragSegment[0]
        const depth = segmentData.depth
        const geometry = segmentData.geometry
        let coordinates = null
        const segment = segmentData.segment
        const index = dragSegment.index

        switch (geometry.geometryType) {
          case Geometry.POINT:
            coordinates = vertex
            segment[0] = segment[1] = vertex
            break
          case Geometry.MULTI_POINT:
            coordinates = geometry.getCoordinates()
            coordinates[segmentData.index] = vertex
            segment[0] = segment[1] = vertex
            break
          case Geometry.LINE:
            if (segmentData.bezier) {
              vertex = evt.originalCoordinate;
              const points = cloneDeep(segmentData.bezierVertex.data.points);
              points[segmentData.bezierVertex.index] = vertex;
              geometry.updateBezierByStartIndex(segmentData.bezierVertex.data.start, points);
              coordinates = geometry.getCoordinates()
            } else {
              coordinates = geometry.getCoordinates()
              if(geometry.lineMode === 2){
                coordinates[segmentData.index][1] = vertex[1]
              }else if(geometry.lineMode === 3){
                coordinates[segmentData.index][0] = vertex[0]
              } else {
                coordinates[segmentData.index] = vertex
              }
            }

            // segment[index] = vertex
            break
          case Geometry.MULTI_LINE:
            coordinates = geometry.getCoordinates()
            coordinates[depth[0]][segmentData.index + index] = vertex
            segment[index] = vertex
            break
          case Geometry.POLYGON:
            if (segmentData.bezier) {
              vertex = evt.originalCoordinate;
              const points = cloneDeep(segmentData.bezierVertex.data.points);
              points[segmentData.bezierVertex.index] = vertex;
              geometry.updateBezierByStartIndex(
                segmentData.bezierVertex.data.start,
                segmentData.bezierVertex.data.ringIndex,
                points
              );
            } else {
              coordinates = geometry.getCoordinates()
              coordinates[segmentData.ringIndex][segmentData.index] = vertex
              if (segmentData.index === 0) {
                coordinates[segmentData.ringIndex][coordinates[segmentData.ringIndex].length - 1] = vertex
              }
            }

            break
          case Geometry.PARALLELOGRAM:
            // 重新计算平行四边的各个顶点
            if(evt.originalEvent.ctrlKey) {
              if (this.map.allowCoordinatesBeyondImage) {
                this._currentMovedGeometry.geometry.rotate(false, this._downPoint[0], this._downPoint[1], vertex[0], vertex[1])
              } else {
                this._currentMovedGeometry.geometry.rotate(this.map.view.dataExtent, this._downPoint[0], this._downPoint[1], vertex[0], vertex[1])
              }
              this._modified = true;
              this.changed()
              this._downPoint = vertex;
              coordinates = null;
            } else {
              coordinates = Parallelogram.updateCoordinatesForModification(geometry,
                vertex, this._oldEditedVertex, dragSegment, this.map.allowCoordinatesBeyondImage ? null : this.map.view._dataExtent)
              this._oldEditedVertex = vertex
            }

            break
          case Geometry.TILTRECTANGLE:
            // 重新计算斜矩形的各个顶点
            if(evt.originalEvent.ctrlKey) {
              if (this.map.allowCoordinatesBeyondImage) {
                this._currentMovedGeometry.geometry.rotate(false, this._downPoint[0], this._downPoint[1], vertex[0], vertex[1])
              } else {
                this._currentMovedGeometry.geometry.rotate(this.map.view.dataExtent, this._downPoint[0], this._downPoint[1], vertex[0], vertex[1])
              }
              this._modified = true;
              this.changed()
              this._downPoint = vertex;
              coordinates = null;
            } else {
              if (this.map.allowCoordinatesBeyondImage) {
                coordinates = TiltRectangle.updateCoordinatesForModification(geometry,
                  vertex, this._oldEditedVertex, dragSegment)
              } else {
                coordinates = TiltRectangle.updateCoordinatesForModification(geometry,
                  vertex, this._oldEditedVertex, dragSegment, this.map.view._dataExtent)
              }
              this._oldEditedVertex = vertex
            }
            break
          case Geometry.TRAPEZIUM:
            // 重新计算斜矩形的各个顶点
            if (this.map.allowCoordinatesBeyondImage) {
              coordinates = Trapezium.updateCoordinatesForModification(geometry,
                vertex, this._oldEditedVertex, dragSegment)
            } else {
              coordinates = Trapezium.updateCoordinatesForModification(geometry,
                vertex, this._oldEditedVertex, dragSegment, this.map.view._dataExtent)
            }
            this._oldEditedVertex = vertex
            break
          case Geometry.SQUAREX:
              // 重新计算斜矩形的各个顶点
              if (this.map.allowCoordinatesBeyondImage) {
                coordinates = SquareX.updateCoordinatesForModification(geometry,
                  vertex, this._oldEditedVertex, dragSegment)
              } else {
                coordinates = SquareX.updateCoordinatesForModification(geometry,
                  vertex, this._oldEditedVertex, dragSegment, this.map.view._dataExtent)
              }
              this._oldEditedVertex = vertex
            break
          case Geometry.EXTENT:
            if (this.map.allowCoordinatesBeyondImage) {
              coordinates = ExtentUtil.updateExtent(geometry, vertex, dragSegment)
            } else {
              coordinates = ExtentUtil.updateExtent(geometry, vertex, dragSegment, this.map.view._dataExtent)
            }
            break
          case Geometry.MULTI_POLYGON:
            coordinates = geometry.getCoordinates()
            coordinates[segmentData.polygonIndex][segmentData.ringIndex][segmentData.index] = vertex
            if (segmentData.index === 0) {
              coordinates[segmentData.polygonIndex][segmentData.ringIndex][coordinates[segmentData.polygonIndex][segmentData.ringIndex].length - 1] = vertex
            }
            break
          case Geometry.CUBE:
            coordinates = geometry.getCoordinates()
            let extent = false;
            if (!this.map.allowCoordinatesBeyondImage) {
              extent = this.map.view.dataExtent
            }
            coordinates = Cube.updateCoordinatesForModification(geometry, segmentData, coordinates, vertex, this._oldEditedVertex, evt, extent)
            this._oldEditedVertex = vertex;
            break
          case Geometry.CIRCLE:
            coordinates = geometry.updateCoordinatesForModification(vertex, this.map.allowCoordinatesBeyondImage ? null : this.map.view._dataExtent);
            break
          case Geometry.ELLIPSE:
            //椭圆
            if(evt.originalEvent.ctrlKey) {
              if (this.map.allowCoordinatesBeyondImage) {
                this._currentMovedGeometry.geometry.rotate(false, this._downPoint[0], this._downPoint[1], vertex[0], vertex[1])
              } else {
                this._currentMovedGeometry.geometry.rotate(this.map.view.dataExtent, this._downPoint[0], this._downPoint[1], vertex[0], vertex[1])
              }
              this._modified = true;
              this.changed()
              this._downPoint = vertex;
              coordinates = null;
            } else {
              coordinates = geometry.getModifyCoordinates(segmentData, vertex, this.map.allowCoordinatesBeyondImage ? null : this.map.view._dataExtent);
            }
            break
          default:
          // pass
        }

        if (coordinates) {
          this._setGeometryCoordinates(geometry, coordinates)
        }

        if (this._displayExtentLwText) {
          if (geometry.geometryType === Geometry.EXTENT || geometry.geometryType === Geometry.TILTRECTANGLE) {
            this.addlwText(geometry)
          }
        }
      }

      this._createOrUpdateVertexFeature(vertex)

    }
    // move the whole selected geometry
    else if (this._currentMovedGeometry) {
      const downPoint = this._downPoint
      const dx = vertex[0] - downPoint[0]
      const dy = vertex[1] - downPoint[1]
      let beyond = null;
      if (!this.map.allowCoordinatesBeyondImage) {
        beyond = {
          xmin: 0,
          ymin: 0,
          xmax: this.map.view.dataExtent[2],
          ymax: this.map.view.dataExtent[3]
        }
      }
      if ( this._currentMovedGeometry.geometry.geometryType === "cube" ) {
        if (evt.originalEvent.shiftKey) {
          this._currentMovedGeometry.geometry.move(dx, dy, {beyond})
        } else {
          try {
            this._currentMovedGeometry.geometry.moveFace(dx, dy, {beyond})
          } catch (error) {

          }

        }

        this._modified = true;
      } else {
        if (!evt.originalEvent.ctrlKey) {
          if (evt.originalEvent.shiftKey) {
            this._currentMovedGeometry.geometry.move(dx, dy, {
              beyond: beyond
            })
          }
          this._modified = true;
        } else if (this._retotableGeometrys.includes(this._currentMovedGeometry.geometry.geometryType)) {
          if (this.map.allowCoordinatesBeyondImage) {
            this._currentMovedGeometry.geometry.rotate(false, this._downPoint[0], this._downPoint[1], vertex[0], vertex[1])
          } else {
            this._currentMovedGeometry.geometry.rotate(this.map.view.dataExtent, this._downPoint[0], this._downPoint[1], vertex[0], vertex[1])
          }
          this._modified = true;
        }
      }
      this.changed()
      this._downPoint = vertex
    }
  }

  addlwText(geometry) {
    try {
      if (this._lwTextFeatures && this._lwTextFeatures.length) {
        this._overLayer.removeFeatures(this._lwTextFeatures)
      }
      let coords = geometry.getCoordinates()
      if (geometry.geometryType === Geometry.TILTRECTANGLE) {
        coords = coords[0]
      }
      const createPoint = (p1, p2) => {
        let c = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2]
        const feature = new Feature(new Point(...c), {}, '', [this._extentLwTextStyle])
        feature.displayText = distance(p1, p2).toFixed(2)
        return feature
      }
      const features = []
      if (coords[0] && coords[1]) {
        const f1 = createPoint(coords[0], coords[1])
        features.push(f1)
      }
      if (coords[1] && coords[2]) {
        const f2 = createPoint(coords[1], coords[2])
        features.push(f2)
      }

      this._overLayer.addFeatures(features)

      this._lwTextFeatures = features

    } catch (error) {}
  }

  /**
   * handleUpEvent
   * @param evt {Event}
   * @returns {boolean}
   * @private
   */
  _handleUpEvent(evt) {
    let modifiedFeature = null
    if (this._dragSegments.length > 0) {
      modifiedFeature = this._dragSegments[0][0].feature.clone()
    }

    this._dragSegments.length = 0
    this._shouldAddToVertexs = false
    this._insertVertices = []

    if (this._vertexFeature) {
      this._overLayer.removeFeature(this._vertexFeature)
      this._vertexFeature = null
    }

    this._oldEditedVertex = null

    this._downPoint = null
    if (this._modified) {
      this.dispatchEvent(new ModifyEvent(ModifyEvent.EventType.MODIFY_END, modifiedFeature, evt))
      this._modified = false
    }

    if (this._lwTextFeatures && this._lwTextFeatures.length) {
      this._overLayer.removeFeatures(this._lwTextFeatures)
    }
    this.dispatchEvent(new ModifyEvent(ModifyEvent.EventType.MODIFY_MOUSE_UP, undefined,evt))



    return false
  }

  /**
   *
   * segmentData
   * @param vertex
   * @private
   */
  _insertVertex(segmentData, vertex) {
    if (!segmentData) {
      return;
    }

    const segment = segmentData.segment
    const feature = segmentData.feature
    const geometry = segmentData.geometry
    const depth = segmentData.depth
    const isVertex = segmentData.isVertex
    let index = segmentData.index
    let coordinates
    let bezier;

    switch (geometry.geometryType) {
      case Geometry.MULTI_LINE:
        coordinates = geometry.getCoordinates()
        coordinates[depth[0]].splice(index + 1, 0, vertex)
        break
      case Geometry.POLYGON:
        coordinates = geometry.getCoordinates()
        if (!isVertex) {

          coordinates[segmentData.ringIndex].splice(index + 1, 0, vertex)
          index = index + 1;

          //新插入点更新贝塞尔索引
          const newBezier = geometry.bezier.map(item => {
            if (item.ringIndex === segmentData.ringIndex) {
              if (item.start >= index) {
                return {
                  start: item.start + 1,
                  ringIndex: item.ringIndex,
                  points: item.points
                }
              } else {
                return {
                  start: item.start,
                  points: item.points,
                  ringIndex: item.ringIndex,
                }
              }
            } else {
              return {
                start: item.start,
                points: item.points,
                ringIndex: item.ringIndex,
              }
            }
          })
          bezier = newBezier;
        } else {
          // dragIndex = index
        }
        break
      case Geometry.MULTI_POLYGON:
        coordinates = geometry.getCoordinates()
        if (!isVertex) {
          coordinates[segmentData.polygonIndex][segmentData.ringIndex].splice(index + 1, 0, vertex)
          index = index + 1
        } else {
          //coordinates[segmentData,polygonIndex][segmentData.ringIndex] = vertex;
          // dragIndex = index
        }
        break
      case Geometry.CUBE:
        coordinates = geometry.getCoordinates()
        break
      case Geometry.LINE:
        coordinates = geometry.getCoordinates()
        if (!isVertex) {
          // 直线不能新增点
           if([2,3].includes(geometry.lineMode)) {
            return
          }
          coordinates.splice(index + 1, 0, vertex)
          index = index + 1
          //新插入点更新贝塞尔索引
          const newBezier = geometry.bezier.map(item => {
            if (item.start >= index) {
              return {
                start: item.start + 1,
                points: item.points
              }
            } else {
              return {
                start: item.start,
                points: item.points
              }
            }
          })
          bezier = newBezier;
        } else {
          // dragIndex = index
        }
        break
      case Geometry.POINT:
        coordinates = geometry.getCoordinates()
        coordinates = vertex
        break
      case Geometry.EXTENT:
        coordinates = geometry.getCoordinates()
        break
      case Geometry.PARALLELOGRAM:
        break
      case Geometry.TILTRECTANGLE:
        break
      case Geometry.TRAPEZIUM:
        break
      case Geometry.SQUAREX:
        break
      case Geometry.CIRCLE:
        break
      case Geometry.ELLIPSE:
        break
      default:
        return
    }

    this._setGeometryCoordinates(geometry, coordinates)
    if (bezier) {
      geometry.setBezier(bezier);
    }
    let newSegmentData;
    if (segmentData.bezier) {
      newSegmentData = segmentData;
    } else {
      newSegmentData = {
        segment: [segment[0], vertex],
        feature,
        geometry,
        depth,
        index,
        ringIndex: segmentData.ringIndex,
        polygonIndex: segmentData.polygonIndex,
        isVertex
      }
    }

    this._dragSegments.push([newSegmentData, 1])
    this._ignoreNextSingleClick = true
  }

  /**
   * Calcalute geometrys within the current extent
   * TODO 将来需要优化计算视图范围内的图形
   *
   * getInExtent
   * @param geometry {Array}
   * @param pixelCoordinate {Array}
   * @param tolarance {Number}
   * @returns {Array}
   * @private
   */
  _getInExtent(features, pixelCoordinate, tolarance) {
    let result = []
    const _ExtentUtil = ExtentUtil
    // loop the passed features
    features.forEach(feature => {
      const geometry = feature.geometry

      // build an extent from the passed geometry with tolarance
      const gometryExtent = geometry.extent
      const bufferExtent = _ExtentUtil.buffer([gometryExtent.xmin, gometryExtent.ymin,
      gometryExtent.xmax, gometryExtent.ymax], tolarance)

      // Exclude the moved point
      if (_ExtentUtil.containsPoint(bufferExtent, pixelCoordinate)) {
        // Calculte if the moved point has insected a geometry
        const geometryType = geometry.geometryType

        if (geometryType === Geometry.POINT) {
          const points = geometry.getCoordinates()
          const dist = distance(points, pixelCoordinate)
          if (dist <= tolarance) {
            result.push({
              feature,
              geometry: geometry,
              segment: [points, points],
              index: 0
            })
          }
        } else if (
          geometryType === Geometry.POLYGON ||
          geometryType === Geometry.PARALLELOGRAM ||
          geometryType === Geometry.TILTRECTANGLE ||
          geometryType === Geometry.TRAPEZIUM ||
          geometryType === Geometry.SQUAREX
        ) { // @todo 这一块需要重构
          let coords = geometry.getCoordinates()
          coords.forEach((coordinates, ringIndex) => {
            for (let j = 0, jj = coordinates.length - 1; j < jj; j++) {
              let points = coordinates[j]
              let nextPoints = coordinates[j + 1]

              let pathExtent = _ExtentUtil.boundingExtentFromTwoPoints(points, nextPoints)
              let pathBufferExtent = _ExtentUtil.buffer(pathExtent, tolarance)

              if (ExtentUtil.containsPoint(pathBufferExtent, pixelCoordinate)) {
                const segment = [points, nextPoints]
                // if (geometryType === Geometry.POLYGON) {
                //   if (!geometry.hasBezier(ringIndex, j)) {
                //     result.push({
                //       feature,
                //       geometry,
                //       segment,
                //       index: j,
                //       ringIndex
                //     })
                //   }
                // } else {
                  result.push({
                    feature,
                    geometry,
                    segment,
                    index: j,
                    ringIndex
                  })
                // }
              }
            }
          })
          if (geometryType === Geometry.POLYGON) {
            const bezierVertex = geometry.getBezierVertex(pixelCoordinate, this._pixelTolerance);

            if (bezierVertex.vertex) {
              result.push({
                feature,
                geometry,
                bezierVertex,
                isVertex: true,
                bezier: true
              })
            }
          }
        } else if (geometryType === Geometry.MULTI_POLYGON || geometryType === Geometry.CUBE) {
          let allCoords = geometry.getCoordinates()
          allCoords.forEach((coords, polygonIndex) => {
            coords.forEach((coordinates, ringIndex) => {
              for (let j = 0, jj = coordinates.length; j < jj; j++) {
                let points = coordinates[j]
                let nextPoints = coordinates[j + 1]
                //解决立方体下边不能选中的问题
                if (j === coordinates.length - 1) {
                  nextPoints = coordinates[0];
                }
                let pathExtent = _ExtentUtil.boundingExtentFromTwoPoints(points, nextPoints)
                let pathBufferExtent = _ExtentUtil.buffer(pathExtent, tolarance)

                if (ExtentUtil.containsPoint(pathBufferExtent, pixelCoordinate)) {
                  const segment = [points, nextPoints]
                  result.push({
                    feature,
                    geometry,
                    segment,
                    polygonIndex,
                    index: j,
                    ringIndex
                  })
                }
              }
            })
          })
        } else if(geometryType === Geometry.LINE) {
          let coordinates = geometry.getCoordinates()

          for (let j = 0, jj = coordinates.length - 1; j < jj; j++) {
            let points = coordinates[j]
            let nextPoints = coordinates[j + 1]

            let pathExtent = _ExtentUtil.boundingExtentFromTwoPoints(points, nextPoints)
            let pathBufferExtent = _ExtentUtil.buffer(pathExtent, tolarance)

            if (ExtentUtil.containsPoint(pathBufferExtent, pixelCoordinate)) {
              const segment = [points, nextPoints]
              // if (!geometry.hasBezier(j)) {
                result.push({
                  feature,
                  geometry,
                  segment,
                  index: j
                })
              // }
            }
          }

          const bezierVertex = geometry.getBezierVertex(pixelCoordinate, this._pixelTolerance);

          if (bezierVertex.vertex) {
            result.push({
              feature,
              geometry,
              bezierVertex,
              isVertex: true,
              bezier: true
            })
          }


        } else if (geometryType === Geometry.EXTENT) {
          let coordinates = geometry.getCoordinates()

          for (let j = 0, jj = coordinates.length - 1; j < jj; j++) {
            let points = coordinates[j]
            let nextPoints = coordinates[j + 1]

            let pathExtent = _ExtentUtil.boundingExtentFromTwoPoints(points, nextPoints)
            let pathBufferExtent = _ExtentUtil.buffer(pathExtent, tolarance)

            if (ExtentUtil.containsPoint(pathBufferExtent, pixelCoordinate)) {
              const segment = [points, nextPoints]
              result.push({
                feature,
                geometry,
                segment,
                index: j
              })
            }
          }
        } else if (geometryType === Geometry.ELLIPSE || geometryType === Geometry.CIRCLE) {

          const points = geometry.getCoordinates()
          if (geometry.containsXY(pixelCoordinate[0], pixelCoordinate[1])) {
            result.push({
              feature,
              geometry: geometry,
              segment: [points, points],
              index: 0
            })
          }
        }
      }
    })

    return result
  }

  /**
   * 设置Ceometry的坐标数据
   *
   * @method setGeometryCoordinates
   * @param geometry {Object}
   * @param coordinates {Array}
   * @private
   */
  _setGeometryCoordinates(geometry, coordinates) {
    if (coordinates) {
      this._changingFeature = true
      geometry.setCoordinates(coordinates)
      this._changingFeature = false
    }
  }

  /**
   * @method pointDistanceToSegment
   *
   * coordinates {Array}
   * @param segmentData
   * @returns {number}
   * @private
   */
  _pointDistanceToSegment(coordinates, segmentData) {
    const geometry = segmentData.geometry

    if (geometry.geometryType === Geometry.CIRCLE) {
      const circleGeometry = geometry

      if (segmentData.index === Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX) {
        const distanceToCenterSquared =
          squaredDistance(circleGeometry.getCenter(), coordinates)
        const distanceToCircumference =
          Math.sqrt(distanceToCenterSquared) - circleGeometry.getRadius()
        return distanceToCircumference * distanceToCircumference
      }
    }


    return squaredDistanceToSegment(coordinates, segmentData.segment)
  }

  /**
   * closestOnSegment
   * @param coordinate
   * @param segmentData
   * @returns {*}
   * @private
   */
  _closestOnSegment(coordinate, segmentData) {
    return closestOnSegment(coordinate, segmentData.segment)
  }

  /**
   * Create or update the vertex feature while snapping a point
   * on the edge of geometry
   *
   * createOrUpdateVertexFeature
   * @param point {Array}
   * @private
   */
  _createOrUpdateVertexFeature(point) {
    let vertexFeature = this._vertexFeature
    if (!vertexFeature) {
      vertexFeature = new Feature(new Point(point[0], point[1]))
      this._vertexFeature = vertexFeature
      this._overLayer.addFeature(vertexFeature)
    } else {
      const geom = vertexFeature.geometry
      geom.update(point[0], point[1])
      this._overLayer.changed()
    }
  }

  /**
   * 获取默认的图形编辑样式
   *
   * @method getDefaultStyleFunction
   * @returns {Function}
   */
  getDefaultStyleFunction() {
    const style = Style.createDefaultEditing()
    return function () {
      return style[Geometry.POINT]
    }
  }

  /**
   *
   * mapBrowserEvent
   * @returns {*}
   * @private
   */
  _defaultDeleteCondition(mapBrowserEvent) {
    return noModifierKeys(mapBrowserEvent) &&
      singleClick(mapBrowserEvent)
  }

  /**
   * active读写器，读取设置当前的active状态
   *
   * @property active
   * @type {Function}
   * @returns {*}
   */
  get active() { return this._active }
  set active(isActive) {
    if (this._vertexFeature && !isActive) {
      this._overLayer.removeFeature(this._vertexFeature)
      this._vertexFeature = null
    }

    this._active = isActive
  }

  /**
   * map读写器, 读取设置当前map
   *
   * @type {Function}
   * @property map
   * @param map {Object} Datatang.map
   */
  get map() { return this._map }
  set map(map) {
    if (this._mapRenderKey) {
      unlistenByKey(this._mapRenderKey)
      this._mapRenderKey = null
    }

    if (map) {
      this._map = map
      this._mapRenderKey = listen(this, EventType.CHANGE, map.render, map)

      map.addLayer(this._overLayer)
    }
  }
}

/**
 *
 * @type {number}
 */
Modify.MODIFY_SEGMENT_CIRCLE_CIRCUMFERENCE_INDEX = 1

/**
 *
 * @type {number}
 */
Modify.MODIFY_SEGMENT_CIRCLE_CENTER_INDEX = 0
